package main

import (
	"errors"
	"fmt"
	"sync/atomic"
	"time"
)

type IResource interface {
	Id() int32
	Close() error
}

type Creator func() (IResource, error)

// LimitedResource 是对象池的结构体
type LimitedResource struct {
	resourcesAvailable chan bool //可用资源channel
	maxSize            int32     //最大个数
	creator            Creator   //资源创建函数

	createCount         int32 //创建资源计数器
	acquireSuccessCount int32 //资源计获取成功数器
	acquireFailedCount  int32 //资源计获取失败数器
}

// Resource 代表池中的资源
type Resource struct {
	id int32
}

func (r *Resource) Id() int32 {
	if r == nil {
		panic("资源对象不能为空: func:Id()")
	}
	return r.id
}

func (r *Resource) Close() error {
	if r == nil {
		panic("资源对象不能为空: func:ClosePool()")
	}
	//调用对于的资源释放函数
	fmt.Printf("关闭资源 id:%d\n", r.Id())
	return nil
}

var resourceCounter int32 //资源计数器，用来定义id

func CreateResource() (IResource, error) {
	newId := atomic.AddInt32(&resourceCounter, 1)
	//这里添加真实资源的创建/打开函数
	return &Resource{newId}, nil
}

// NewLimitedResource 创建一个对象池
func NewLimitedResource(maxSize int, creator Creator) *LimitedResource {
	lr := &LimitedResource{
		resourcesAvailable: make(chan bool, maxSize),
		creator:            creator,
	}
	for i := 0; i < maxSize; i++ {
		lr.resourcesAvailable <- true
	}

	return lr
}

// Borrow 借用一个资源
func (lr *LimitedResource) Borrow(timeout time.Duration) (IResource, error) {
	select {
	case _ = <-lr.resourcesAvailable:
		atomic.AddInt32(&lr.acquireSuccessCount, 1)
		newResource, err := lr.creator()
		if err != nil {
			atomic.AddInt32(&lr.acquireFailedCount, 1)
			return nil, err
		}
		atomic.AddInt32(&lr.createCount, 1)
		return newResource, nil
	case <-time.After(timeout):
		fmt.Println("获取资源超时")
		atomic.AddInt32(&lr.acquireFailedCount, 1)
		return nil, errors.New("timeout")
	}
}

// Return 归还一个资源回到池中
func (lr *LimitedResource) Return(r IResource) error {
	select {
	case lr.resourcesAvailable <- true:
		err := r.Close()
		if err != nil {
			return err
		}
		fmt.Printf("资源 id:%d 返还给资源池\n", r.Id())
	default:
		fmt.Printf("理论上调不到次处===>资源 id:%d 必须丢弃，资源队列以及满\n", r.Id())
		err := r.Close()
		if err != nil {
			return err
		}
		return errors.New("返还出错，资源channel 满")
	}
	return nil
}

func Test() {
	pool := NewLimitedResource(3, CreateResource)

	// 模拟借用和归还资源
	for i := 0; i < 10; i++ {
		go func(i int) {
			r, err := pool.Borrow(7 * time.Second) // 超时时间设置为 7 秒
			if err != nil {
				fmt.Printf("Goroutine %d 获取资源失败 err:%s\n", i, err.Error())
				return
			}
			fmt.Printf("Goroutine %d 成功获取资源 id:%d\n", i, r.Id())
			time.Sleep(3 * time.Second) // 模拟资源使用 3秒
			err = pool.Return(r)
			if err != nil {
				fmt.Printf("返还资源出错:%s\n", err.Error())
			}
		}(i)
	}

	// 等待所有 Goroutine 完成 任务
	time.Sleep(15 * time.Second)
	fmt.Printf("Pool 创建对象 %v 次\n获取资源对象成功 %v 次\nPool获取资源对象失败 %v 次\n",
		pool.createCount,
		pool.acquireSuccessCount,
		pool.acquireFailedCount)
}

func main() {
	Test() //测试对象在返还的时候直接销毁
}
